package com.brotherly.util;

import java.net.URI;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;

public class URIBuilder implements Cloneable
{
    protected String scheme = "http";

    protected String username = "";
    protected String password = "";

    protected String host = "127.0.0.1";
    protected int port = -1;

    protected String frament = "";

    protected PathBuilder path = new PathBuilder();

    public class PathBuilder implements Cloneable
    {
        protected String encoding = "utf-8";
        protected LinkedList<String> paths = new LinkedList<String>();
        protected HashMap<String, String> query = new HashMap<String, String>();

        public PathBuilder()
        {}

        public PathBuilder(String path)
        {
            String query = null;

            if (path == null)
                return;

            int index = path.indexOf('?');
            if (index != -1)
            {
                query = path.substring(index + 1);
                path = path.substring(0, index);
            }

            if (path.length() != 0)
            {
                path = path.replace('\\', '/');

                String[] array = path.split("/");
                for (index = 0; index != array.length; ++index)
                {
                    String name = array[index];

                    if (name.length() != 0)
                        paths.add(name);
                }
            }

            if (query != null && query.length() != 0)
            {
                String[] tuples = query.split("&");

                for (index = 0; index != tuples.length; ++index)
                {
                    String name = "", value = "";
                    String tuple = tuples[index];

                    int pos = tuple.indexOf('=');

                    if (pos != -1)
                    {
                        value = tuple.substring(pos + 1);
                    } else
                    {
                        pos = tuple.length();
                    }

                    name = tuple.substring(0, pos);

                    this.query.put(name, value);
                }
            }
        }

        @SuppressWarnings(
        {
                "unchecked"
        })
        public PathBuilder clone()
        {
            PathBuilder res = new PathBuilder();

            res.encoding = this.encoding;
            res.paths = (LinkedList<String>) this.paths.clone();
            res.query = (HashMap<String, String>) this.query.clone();

            return res;
        }

        public int size()
        {
            return paths.size();
        }

        public PathBuilder pushPath(String name)
        {
            paths.add(name);

            return this;
        }

        public PathBuilder popPath()
        {
            paths.removeLast();
            return this;
        }

        public PathBuilder putParameter(String key, String value)
        {
            query.put(key, value);

            return this;
        }

        public PathBuilder removeParameter(String key)
        {
            query.remove(key);

            return this;
        }

        public String toString()
        {
            StringBuilder builder = new StringBuilder();

            Iterator<String> itPath = paths.iterator();
            while (itPath.hasNext())
            {
                String name = itPath.next();

                builder.append('/');
                builder.append(name);
            }

            if (query.size() != 0)
            {
                boolean needDelimiter = false;
                builder.append('?');

                Iterator<Entry<String, String>> itEntry = query.entrySet().iterator();

                while (itEntry.hasNext())
                {
                    Entry<String, String> entry = itEntry.next();

                    String name = entry.getKey();
                    String value = entry.getValue();

                    if (needDelimiter)
                        builder.append('&');

                    builder.append(name);
                    if (value.length() != 0)
                    {
                        builder.append('=');

                        try
                        {
                            builder.append(URLEncoder.encode(value, encoding));
                        }
                        catch (Exception e)
                        {
                            builder.append(value);
                        }
                    }

                    needDelimiter = true;
                }
            }

            return builder.toString();
        }

        public String getEncoding()
        {
            return encoding;
        }

        public void setEncoding(String encoding)
        {
            this.encoding = encoding;
        }
    }

    public URIBuilder()
    {}

    public URIBuilder(String url)
    {
        try
        {
            URI uri = new URI(url);

            scheme = uri.getScheme();
            username = uri.getUserInfo();
            host = uri.getHost();
            port = uri.getPort();

            if (uri.getQuery() != null)
            {
                path = new PathBuilder(uri.getPath() + '?' + uri.getQuery());
            } else
            {
                path = new PathBuilder(uri.getPath());
            }

            frament = uri.getFragment();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        if (username == null)
            username = "";

        if (frament == null)
            frament = "";

        int index = username.indexOf(':');
        if (index != -1)
        {
            password = username.substring(index + 1);
            username = username.substring(0, index);
        }
    }

    public URIBuilder(String scheme, String host, short port, String path)
    {
        this.setScheme(scheme);
        this.setHost(host);
        this.setPort(port);

        this.setPath(new PathBuilder(path));
    }

    public String getFrament()
    {
        return frament;
    }

    public void setFrament(String frament)
    {
        this.frament = frament;
    }

    public URIBuilder clone()
    {
        URIBuilder res = new URIBuilder();

        res.scheme = this.scheme;
        res.username = this.username;
        res.password = this.password;
        res.host = this.host;
        res.port = this.port;
        res.path = this.path.clone();
        res.frament = this.frament;

        return res;
    }

    public String getUsername()
    {
        return username;
    }

    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getScheme()
    {
        return scheme;
    }

    public void setScheme(String scheme)
    {
        this.scheme = scheme;
    }

    public int getPort()
    {
        return port;
    }

    public void setPort(int port)
    {
        this.port = port;
    }

    public void setPath(PathBuilder path)
    {
        this.path = path;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    public String getHost()
    {
        return host;
    }

    public void setHost(String host)
    {
        this.host = host;
    }

    public PathBuilder getPath()
    {
        return path;
    }

    public String toString()
    {
        StringBuilder builder = new StringBuilder();

        builder.append(getScheme());
        builder.append("://");

        if (getUsername().length() != 0)
        {
            builder.append(getUsername());

            if (getPassword().length() != 0)
            {
                builder.append(':');
                builder.append(getPassword());
            }

            builder.append('@');
        }

        builder.append(getHost());

        if (getPort() != -1)
        {
            builder.append(':');
            builder.append(getPort());
        }

        builder.append(getPath().toString());

        if (frament.length() != 0)
        {
            builder.append('#');
            builder.append(frament);
        }

        return builder.toString();
    }

    public String encode(String encoding)
    {
        StringBuilder builder = new StringBuilder();

        builder.append(getScheme());
        builder.append("://");

        if (getUsername().length() != 0)
        {
            builder.append(getUsername());

            if (getPassword().length() != 0)
            {
                builder.append(':');
                builder.append(getPassword());
            }

            builder.append('@');
        }

        builder.append(getHost());

        if (getPort() != -1)
        {
            builder.append(':');
            builder.append(getPort());
        }

        try
        {
            builder.append(URLEncoder.encode(getPath().toString(), "utf-8"));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        if (frament.length() != 0)
        {
            builder.append('#');
            builder.append(frament);
        }

        return builder.toString();
    }
}
